package com.lbc.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

//  懒汉式单例
//  道高一尺魔高一丈
public class LazyMan {

    private static boolean lbc = false;  //  没有谁知道这个变量的名字叫 lbc

//    private LazyMan() {
//        synchronized (LazyMan.class) {
//            if (lazyMan!=null) {
//                throw new RuntimeException("不要试图使用反射破坏单例异常");
//            }
//        }
//        System.out.println(Thread.currentThread().getName() + " ok");
//    }
    private LazyMan() {
        synchronized (LazyMan.class) {
            if (lbc == false) {
                lbc = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏单例异常");
            }
        }
        System.out.println(Thread.currentThread().getName() + " ok");
    }

    private volatile static LazyMan lazyMan;

    //  双重检测锁模式的 懒汉式单例   DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan==null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();  //  不是原子性操作
                }
            }
        }
        return lazyMan;
    }
    /**
     * 1、分配内存空间
     * 2、执行构造方法，初始化对象
     * 3、把这个对象指向这个空间
     *
     * 123
     * 132  A
     *      B // 此时lazyMan还没有完成构造
     */

    //  单线程下确实ok, 多线程并发下有问题
//    public static void main(String[] args) {
//        for (int i = 0; i < 100; i++) {
//            new Thread(()->{
//                lazyMan.getInstance();
//            }).start();
//        }
//    }

    //  反射！
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//        LazyMan instance1 = LazyMan.getInstance();

        //  让我们把 lbc 的私有性也给破坏了吧
        Field lbc = LazyMan.class.getDeclaredField("lbc");
        lbc.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();

        lbc.set(instance2, false);

        LazyMan instance3 = declaredConstructor.newInstance();

//        System.out.println(instance1);
        System.out.println(instance2);
        System.out.println(instance3);
    }
}
